home *** CD-ROM | disk | FTP | other *** search
/ C/C++ Users Group Library 1996 July / C-C++ Users Group Library July 1996.iso / vol_100 / 182_01 / sb_217.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-07-30  |  13.3 KB  |  639 lines

  1. #define VERSION "sb 2.17 07-02-85"
  2. #define PUBDIR "/usr/spool/uucppublic"
  3.  
  4. /*% cc -O -DUSG sb.c -o sb
  5.  
  6.  * sb.c By Chuck Forsberg
  7.  *
  8.  * A small program for Unix which can send 1 or more
  9.  * files in Batch mode to computers running YAM. (Use "rb" in YAM.)
  10.  * Supports the CRC option or regular checksum.
  11.  * Exit status is 0 for all transfers completed successfully,
  12.  * 1 for 1 or more unreadable files or'd with 200 for incomplete file xfer.
  13.  *
  14.  * accepts -k option for 1kb record length.
  15.  *
  16.  * sb is derived from yam2.c
  17.  * Uses buffered i/o to reduce the CPU time compared to UMODEM.
  18.  *  USG UNIX (3.0) ioctl conventions courtesy  Jeff Martin
  19.  *     cc -O -DV7  sb.c -o sb        vanilla Unix version 7
  20.  *    cc -O -DUSG sb.c -o sb        USG (3.0) Unix
  21.  *  ******* Some systems (Venix, Coherent, Regulus) do not *******
  22.  *  ******* support tty raw mode read(2) identically to    *******
  23.  *  ******* Unix. ONEREAD must be defined to force one     *******
  24.  *  ******* character reads for these systems.           *******
  25.  */
  26.  
  27. #define LOGFILE "/tmp/sblog"
  28.  
  29. #include <stdio.h>
  30. #include <signal.h>
  31. #include <setjmp.h>
  32. #include <ctype.h>
  33.  
  34. #define PATHLEN 256
  35. #define OK 0
  36. #define FALSE 0
  37. #define TRUE 1
  38. #define ERROR (-1)
  39.  
  40. #define HOWMANY 2
  41. #include "rbsb.c"    /* most of the system dependent stuff here */
  42.  
  43. FILE *in;
  44.  
  45. /* Ward Christensen / CP/M parameters - Don't change these! */
  46. #define ENQ 005
  47. #define CAN ('X'&037)
  48. #define XOFF ('s'&037)
  49. #define XON ('q'&037)
  50. #define SOH 1
  51. #define STX 2
  52. #define EOT 4
  53. #define ACK 6
  54. #define NAK 025
  55. #define CPMEOF 032
  56. #define WANTCRC 0103    /* send C not NAK to get crc not checksum */
  57. #define WANTG 0107    /* Send G not NAK to get nonstop batch xmsn */
  58. #define TIMEOUT (-2)
  59. #define ERRORMAX 5
  60. #define RETRYMAX 5
  61. #define SECSIZ 128    /* cp/m's Magic Number record size */
  62. #define KSIZE 1024
  63.  
  64. char Lastrx;
  65. char Crcflg;
  66. int Wcsmask=0377;
  67. int Verbose=0;
  68. int Restricted=0;    /* restricted; no /.. or ../ in filenames */
  69. int Quiet=0;        /* overrides logic that would otherwise set verbose */
  70. int Fullname=0;        /* transmit full pathname */
  71. int Unlinkafter=0;    /* Unlink file after it is sent */
  72. int Dottoslash=0;    /* Change foo.bar.baz to foo/bar/baz */
  73. int firstsec;
  74. int errcnt=0;        /* number of files unreadable */
  75. int blklen=SECSIZ;    /* length of transmitted records */
  76. int Optiong;        /* Let it rip no wait for sector ACK's */
  77. int Noeofseen;
  78. int Totsecs;        /* total number of sectors this file */
  79. char txbuf[KSIZE];
  80. int Filcnt=0;        /* count of number of files opened */
  81.  
  82. jmp_buf tohere;        /* For the interrupt on RX timeout */
  83.  
  84. unsigned updcrc();
  85. char *substr(), *getenv();
  86.  
  87. /* called by signal interrupt or terminate to clean things up */
  88. bibi(n)
  89. {
  90.     canit(); fflush(stdout); mode(0);
  91.     fprintf(stderr, "sb: caught signal %d; exiting\n", n);
  92.     if (n == SIGQUIT)
  93.         abort();
  94.     exit(128+n);
  95. }
  96.  
  97. #ifdef REGULUS
  98. sendline(c)
  99. {
  100.     static char d[2];
  101.  
  102.     d[0] = c&Wcsmask;
  103.     write(1, d, 1);
  104. }
  105. #else
  106. #define sendline(c) putchar(c & Wcsmask)
  107. #endif
  108.  
  109. main(argc, argv)
  110. char *argv[];
  111. {
  112.     register char *cp;
  113.     register npats;
  114.     int agcnt; char **agcv;
  115.     char **patts;
  116.     int exitcode;
  117. #ifndef REGULUS
  118.     static char xXbuf[BUFSIZ];
  119. #endif
  120.  
  121.     if ((cp=getenv("SHELL")) && substr(cp, "rsh"))
  122.         Restricted=TRUE;
  123.  
  124.     npats=0;
  125.     if (argc<2)
  126.         goto usage;
  127. #ifndef REGULUS
  128.     setbuf(stdout, xXbuf);        
  129. #endif
  130.     while (--argc) {
  131.         cp = *++argv;
  132.         if (*cp == '-') {
  133.             while ( *++cp) {
  134.                 switch(*cp) {
  135.                 case '1':
  136.                     iofd = 1; break;
  137.                 case '7':
  138.                     Wcsmask=0177; break;
  139.                 case 'd':
  140.                     ++Dottoslash;
  141.                     /* **** FALL THROUGH TO **** */
  142.                 case 'f':
  143.                     Fullname=TRUE; break;
  144.                 case 'k':
  145.                     blklen=KSIZE; break;
  146.                 case 'q':
  147.                     Quiet=TRUE; Verbose=0; break;
  148.                 case 'u':
  149.                     ++Unlinkafter; break;
  150.                 case 'v':
  151.                     ++Verbose; break;
  152.                 default:
  153.                     goto usage;
  154.                 }
  155.             }
  156.         }
  157.         else if ( !npats && argc>0) {
  158.             if (argv[0][0]) {
  159.                 npats=argc;
  160.                 patts=argv;
  161.             }
  162.         }
  163.     }
  164.     if (npats < 1) {
  165. usage:
  166.         fprintf(stderr,"%s by Chuck Forsberg\n", VERSION);
  167.         fprintf(stderr,"Usage: sb [-7dfkquv] file ...\n");
  168.         exit(1);
  169.     }
  170.     
  171.     if (Verbose) {
  172.         if (freopen(LOGFILE, "a", stderr)==NULL) {
  173.             printf("Can't open log file %s\n",LOGFILE);
  174.             exit(0200);
  175.         }
  176.         setbuf(stderr, NULL);
  177.     }
  178.     if (fromcu() && !Quiet) {
  179.         if (Verbose == 0)
  180.             Verbose = 2;
  181.     }
  182.     if (Verbose != 1) {
  183.         fprintf(stderr, "sb: %d file%s requested:\r\n",
  184.          npats, npats>1?"s":"");
  185.         for ( agcnt=npats, agcv=patts; --agcnt>=0; ) {
  186.             fprintf(stderr, "%s ", *agcv++);
  187.         }
  188.     }
  189.  
  190.     mode(1);
  191.     if (signal(SIGINT, bibi) == SIG_IGN) {
  192.         signal(SIGINT, SIG_IGN); signal(SIGKILL, SIG_IGN);
  193.     }
  194.     else {
  195.         signal(SIGINT, bibi); signal(SIGKILL, bibi);
  196.         signal(SIGQUIT, bibi);
  197.     }
  198.  
  199.     if (wcsend(npats, patts)==ERROR) {
  200.         exitcode=0200;
  201.         canit();
  202.     }
  203.     fflush(stdout);
  204.     mode(0);
  205.     exit((errcnt != 0) | exitcode);
  206. }
  207.  
  208. wcsend(argc, argp)
  209. char *argp[];
  210. {
  211.     register n;
  212.  
  213.     Crcflg=FALSE;
  214.     firstsec=TRUE;
  215.     for (n=0; n<argc; ++n) {
  216.         Totsecs = 0;
  217.         if (wcs(argp[n])==ERROR)
  218.             goto fubar;
  219.     }
  220.     Totsecs = 0;
  221.     if (Filcnt==0) {    /* bitch if we couldn't open ANY files */
  222.         fprintf(stderr,"\r\nCan't open any requested files.\n");
  223.         return ERROR;
  224.     }
  225.     else if (wctxpn("")==ERROR)
  226.         goto fubar;
  227.     return OK;
  228. fubar:
  229.     canit(); return ERROR;
  230. }
  231.  
  232. wcs(oname)
  233. char *oname;
  234. {
  235.     register c;
  236.     register char *p;
  237.     struct stat f;
  238.     char name[PATHLEN];
  239.  
  240.     strcpy(name, oname);
  241.  
  242.     if (Restricted) {
  243.         /* restrict pathnames to current tree or uucppublic */
  244.         if ( substr(name, "../")
  245.          || (name[0]== '/' && strncmp(name, PUBDIR, strlen(PUBDIR))) ) {
  246.             canit();
  247.             fprintf(stderr,"\r\nsb:\tSecurity Violation\r\n");
  248.             return ERROR;
  249.         }
  250.     }
  251.  
  252.     if ((in=fopen(oname, "r"))==NULL) {
  253.         ++errcnt;
  254.         return OK;    /* pass over it, there may be others */
  255.     }
  256.     ++Noeofseen;
  257.     /* Check for directory or block special files */
  258. #ifndef REGULUS        /* This doesn't seem to work on Regulus */
  259.     fstat(fileno(in), &f);
  260.     c = f.st_mode & S_IFMT;
  261.     if (c == S_IFDIR || c == S_IFBLK) {
  262.         fclose(in);
  263.         return OK;
  264.     }
  265. #endif
  266.     ++Filcnt;
  267.     if (wctxpn(name)== ERROR)
  268.         return ERROR;
  269.     if (wctx()==ERROR)
  270.         return ERROR;
  271.     if (Unlinkafter)
  272.         unlink(oname);
  273.     return 0;
  274. }
  275.  
  276. /*
  277.  * generate and transmit pathname block consisting of
  278.  *  pathname (null terminated),
  279.  *  file length, mode time and file mode in octal
  280.  *  as provided by the Unix fstat call.
  281.  *  N.B.: modifies the passed name, may extend it!
  282.  */
  283. wctxpn(name)
  284. char *name;
  285. {
  286.     register firstch;
  287.     register char *p, *q;
  288.     char name2[PATHLEN];
  289.     struct stat f;
  290.  
  291.     logent("\r\nAwaiting pathname nak for %s\r\n", *name?name:"<END>");
  292.     if (getnak())
  293.         return ERROR;
  294.  
  295.     q = (char *) 0;
  296.     if (Dottoslash) {        /* change . to . */
  297.         for (p=name; *p; ++p) {
  298.             if (*p == '/')
  299.                 q = p;
  300.             else if (*p == '.')
  301.                 *(q=p) = '/';
  302.         }
  303.         if (q && strlen(++q) > 8) {    /* If name>8 chars */
  304.             q += 8;            /*   make it .ext */
  305.             strcpy(name2, q);    /* save excess of name */
  306.             *q = '.';
  307.             strcpy(++q, name2);    /* add it back */
  308.         }
  309.     }
  310.  
  311.     for (p=name, q=txbuf ; *p; )
  312.         if ((*q++ = *p++) == '/' && !Fullname)
  313.             q = txbuf;
  314.     *q++ = 0;
  315.     p=q;
  316.     while (q < (txbuf + KSIZE))
  317.         *q++ = 0;
  318. #ifndef REGULUS        /* This doesn't seem to work on Regulus */
  319.     if (*name && fstat(fileno(in), &f)!= -1)
  320.         sprintf(p, "%lu %lo %o", f.st_size, f.st_mtime, f.st_mode);
  321. #endif
  322.     /* force 1k blocks if name won't fit in 128 byte block */
  323.     if (strlen(txbuf) > 127)
  324.         blklen=KSIZE;
  325.     if (wcputsec(txbuf, 0, SECSIZ)==ERROR)
  326.         return ERROR;
  327.     return OK;
  328. }
  329.  
  330. getnak()
  331. {
  332.     register firstch;
  333.  
  334.     Lastrx = 0;
  335.     for (;;) {
  336.         switch (firstch = readock(800,2)) {
  337.         case TIMEOUT:
  338.             logent("Timeout on pathname\n");
  339.             return TRUE;
  340.         case WANTG:
  341.             mode(2);    /* Set cbreak, XON/XOFF, etc. */
  342.             Optiong = TRUE;
  343.         case WANTCRC:
  344.             Crcflg = TRUE;
  345.         case NAK:
  346.             return FALSE;
  347.         case CAN:
  348.             if (Lastrx == CAN)
  349.                 return TRUE;
  350.         default:
  351.             break;
  352.         }
  353.         Lastrx = firstch;
  354.     }
  355. }
  356.  
  357.  
  358. wctx()
  359. {
  360.     register int sectnum, attempts, firstch;
  361.  
  362.     firstsec=TRUE;
  363.  
  364.     while ((firstch=readock(400, 2))!=NAK && firstch != WANTCRC
  365.       && firstch!=TIMEOUT && firstch!=CAN)
  366.         ;
  367.     if (firstch==CAN) {
  368.         logent("Receiver CANcelled\n");
  369.         return ERROR;
  370.     }
  371.     if (firstch==WANTCRC)
  372.         Crcflg=TRUE;
  373.     sectnum=1;
  374.     while (filbuf(txbuf, blklen)) {
  375.         if (wcputsec(txbuf, sectnum, blklen)==ERROR) {
  376.             return ERROR;
  377.         } else
  378.             sectnum++;
  379.     }
  380.     if (Verbose>1)
  381.         fprintf(stderr, " Closing ");
  382.     fclose(in);
  383.     attempts=0;
  384.     do {
  385.         logent(" EOT ");
  386.         purgeline();
  387.         sendline(EOT);
  388.         fflush(stdout);
  389.         ++attempts;
  390.     }
  391.         while ((firstch=(readock(100, 2)) != ACK) && attempts < RETRYMAX);
  392.     if (attempts == RETRYMAX) {
  393.         logent("No ACK on EOT\n");
  394.         return ERROR;
  395.     }
  396.     else
  397.         return OK;
  398. }
  399.  
  400. wcputsec(buf, sectnum, cseclen)
  401. char *buf;
  402. int sectnum;
  403. int cseclen;    /* data length of this sector to send */
  404. {
  405.     register checksum, wcj;
  406.     register char *cp;
  407.     unsigned oldcrc;
  408.     int firstch;
  409.     int attempts;
  410.  
  411.     firstch=0;    /* part of logic to detect CAN CAN */
  412.  
  413.     if (Verbose>1)
  414.         fprintf(stderr, "\rSector %3d %2dk ", Totsecs, Totsecs/8 );
  415.     for (attempts=0; attempts <= RETRYMAX; attempts++) {
  416.         Lastrx= firstch;
  417.         sendline(cseclen==KSIZE?STX:SOH);
  418.         sendline(sectnum);
  419.         sendline(-sectnum -1);
  420.         oldcrc=checksum=0;
  421.         for (wcj=cseclen,cp=buf; --wcj>=0; ) {
  422.             sendline(*cp);
  423.             oldcrc=updcrc(*cp, oldcrc);
  424.             checksum += *cp++;
  425.         }
  426.         if (Crcflg) {
  427.             oldcrc=updcrc(0,updcrc(0,oldcrc));
  428.             sendline((int)oldcrc>>8);
  429.             sendline((int)oldcrc);
  430.         }
  431.         else
  432.             sendline(checksum);
  433.  
  434.         if (Optiong) {
  435.             firstsec = FALSE; return OK;
  436.         }
  437.         firstch = readock(400, (Noeofseen&§num) ? 2:1);
  438. gotnak:
  439.         switch (firstch) {
  440.         case CAN:
  441.             if(Lastrx == CAN) {
  442. cancan:
  443.                 logent("Cancelled\n");  return ERROR;
  444.             }
  445.             break;
  446.         case TIMEOUT:
  447.             logent("Timeout on sector ACK\n"); continue;
  448.         case WANTCRC:
  449.             if (firstsec)
  450.                 Crcflg = TRUE;
  451.         case NAK:
  452.             logent("NAK on sector\n"); continue;
  453.         case ACK: 
  454.             firstsec=FALSE;
  455.             Totsecs += (cseclen>>7);
  456.             return OK;
  457.         case ERROR:
  458.             logent("Got burst for sector ACK\n"); break;
  459.         default:
  460.             logent("Got %02x for sector ACK\n", firstch); break;
  461.         }
  462.         for (;;) {
  463.             Lastrx = firstch;
  464.             if ((firstch = readock(400, 2)) == TIMEOUT)
  465.                 break;
  466.             if (firstch == NAK || firstch == WANTCRC)
  467.                 goto gotnak;
  468.             if (firstch == CAN && Lastrx == CAN)
  469.                 goto cancan;
  470.         }
  471.     }
  472.     logent("Retry Count Exceeded\n");
  473.     return ERROR;
  474. }
  475.  
  476.  
  477. /* fill buf with count chars padding with ^Z for CPM */
  478. filbuf(buf, count)
  479. register char *buf;
  480. {
  481.     register c, m;
  482.     m=count;
  483.     while ((c=getc(in))!=EOF) {
  484.         *buf++ =c;
  485.         if (--m == 0)
  486.             break;
  487.     }
  488.     if (m==count)
  489.         return (Noeofseen=0);
  490.     else
  491.         while (--m>=0)
  492.             *buf++ = CPMEOF;
  493.     return count;
  494. }
  495.  
  496.  
  497. alrm()
  498. {
  499.     longjmp(tohere, -1);
  500. }
  501.  
  502.  
  503. /*
  504.  * readock(timeout, count) reads character(s) from file descriptor 0
  505.  *  (1 <= count <= 3)
  506.  * it attempts to read count characters. If it gets more than one,
  507.  * it is an error unless all are CAN
  508.  * (otherwise, only normal response is ACK, CAN, or C)
  509.  *
  510.  * timeout is in tenths of seconds
  511.  */
  512. readock(timeout, count)
  513. {
  514.     register int c;
  515.     static char byt[5];
  516.  
  517.     fflush(stdout);
  518.     if (setjmp(tohere)) {
  519.         logent("TIMEOUT\n");
  520.         return TIMEOUT;
  521.     }
  522.     c = timeout/10;
  523.     if (c<2)
  524.         c=2;
  525.     if (Verbose>3)
  526.         fprintf(stderr, "Timeout=%d Calling alarm(%d) ", timeout, c);
  527.     signal(SIGALRM, alrm); alarm(c);
  528. #ifdef ONEREAD
  529.     c=read(iofd, byt, 1);        /* regulus raw read is unique */
  530. #else
  531.     c=read(iofd, byt, count);
  532. #endif
  533.     alarm(0);
  534.     if (Verbose>5)
  535.         fprintf(stderr, "ret cnt=%d %x %x\n", c, byt[0], byt[1]);
  536.     if (c<1)
  537.         return TIMEOUT;
  538.     if (c==1)
  539.         return (byt[0]&0377);
  540.     else
  541.         while (c)
  542.             if (byt[--c] != CAN)
  543.                 return ERROR;
  544.     return CAN;
  545. }
  546.  
  547. purgeline()
  548. {
  549. #ifdef USG
  550.     ioctl(iofd, TCFLSH, 0);
  551. #else
  552.     lseek(iofd, 0L, 2);
  553. #endif
  554. }
  555.  
  556. /* update CRC */
  557. unsigned updcrc(c, crc)
  558. register c;
  559. register unsigned crc;
  560. {
  561.     register count;
  562.  
  563.     for (count=8; --count>=0;) {
  564.         if (crc & 0x8000) {
  565.             crc <<= 1;
  566.             crc += (((c<<=1) & 0400)  !=  0);
  567.             crc ^= 0x1021;
  568.         }
  569.         else {
  570.             crc <<= 1;
  571.             crc += (((c<<=1) & 0400)  !=  0);
  572.         }
  573.     }
  574.     return crc;    
  575. }
  576.  
  577. /* send 10 CAN's to try to get the other end to shut up */
  578. canit()
  579. {
  580.     register n;
  581.     for (n=10; --n>=0; )
  582.         sendline(CAN);
  583. }
  584.  
  585. #ifdef REGULUS
  586. /*
  587.  * copies count bytes from s to d
  588.  * (No structure assignment in Regulus C compiler)
  589.  */
  590. movmem(s, d, count)
  591. register char *s, *d;
  592. register count;
  593. {
  594.     while (--count >= 0)
  595.         *d++ = *s++;
  596. }
  597. #endif
  598.  
  599. /*VARARGS1*/
  600. logent(a, b, c)
  601. char *a, *b, *c;
  602. {
  603.     if(Verbose)
  604.         fprintf(stderr, a, b, c);
  605. }
  606.  
  607. /*
  608.  * return 1 iff stdout and stderr are different devices
  609.  *  indicating this program operating with a modem on a
  610.  *  different line
  611.  */
  612. fromcu()
  613. {
  614.     struct stat a, b;
  615.     fstat(1, &a); fstat(2, &b);
  616.     return (a.st_rdev != b.st_rdev);
  617. }
  618.  
  619. /*
  620.  * substr(string, token) searches for token in string s
  621.  * returns pointer to token within string if found, NULL otherwise
  622.  */
  623. char *
  624. substr(s, t)
  625. register char *s,*t;
  626. {
  627.     register char *ss,*tt;
  628.     /* search for first char of token */
  629.     for (ss=s; *s; s++)
  630.         if (*s == *t)
  631.             /* compare token with substring */
  632.             for (ss=s,tt=t; ;) {
  633.                 if (*tt == 0)
  634.                     return s;
  635.                 if (*ss++ != *tt++)
  636.                     break;
  637.             }
  638.     return NULL;
  639. }